home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / WINER.ZIP / CHAP4.TXT < prev    next >
Text File  |  1994-09-04  |  60KB  |  1,054 lines

  1.  
  2.                                  CHAPTER 4
  3.  
  4.                            DEBUGGING STRATEGIES
  5.  
  6.  
  7. There are many individual components which contribute to a completed
  8. application.  The logical flow of the program must be determined, the user
  9. interface must be designed, and appropriate algorithms must be selected. 
  10. But no matter how much effort you devote to the design and implementation
  11. of a program, the bottom line is it must also work correctly.
  12.      In an ideal scenario, you would begin writing a program by first
  13. jotting down some notes that describe its operation.  Next, you would
  14. create an outline listing each of the program's major components.  You
  15. would then determine all of the subroutines and functions that are needed,
  16. and perhaps even create a flow chart showing each of the paths that could
  17. be taken.  Properly prepared for any situation that might arise, you
  18. finally write the actual code and find that it works perfectly.  Now,
  19. what's wrong with this picture?  Few people actually program that way!
  20.      In practice, many programmers simply start coding with little
  21. forethought and no detailed plan.  They begin with the first statement and
  22. continue to the last, occasionally reworking portions into subroutines as
  23. necessary.  After all, planning is not nearly as much fun as programming,
  24. and everyone knows that fun is the most important part.  Believe it or not,
  25. I agree.  There's nothing really wrong with plodding through a program,
  26. stabbing here and there until it works.  Indeed, some great algorithms
  27. developed out of aimless doodling.  I have personally never drawn a flow
  28. chart, and I have no plans to start now.  
  29.      What I will address here is how to find and correct problems when they
  30. do occur.  There are more things that can go wrong with a program than can
  31. go right, and tracking down an elusive "Illegal function call" error that
  32. appears only occasionally is definitely not much fun.  How quickly you can
  33. solve these problems is directly related to your understanding of
  34. programming in general, and to your familiarity with the tools available.
  35.      In this chapter you will learn how to identify problems in your
  36. programs, and also how to solve them.  Programming errors, or bugs, can be
  37. as simple as a misspelled variable name, and as complex and ornery as an
  38. internal flaw in BASIC itself.  The BASIC editing environment provides a
  39. wealth of powerful debugging features, and understanding how to use them
  40. will help you produce programs that are reliable and error free.
  41.  
  42.  
  43. COMMON PROGRAMMING ERRORS
  44. =========================
  45.  
  46. There are three distinct types of programming errors: simple misspellings
  47. and other naming or syntax errors, incorrect logic such as misunderstanding
  48. or incorrectly coding an algorithm, and failing to understand some of the
  49. finer points of the BASIC language.  No matter how carefully you type, no
  50. matter how much forethought you apply to a particular problem, and no
  51. matter how often you read the BASIC manuals, it is impossible to completely
  52. avoid making mistakes.
  53.      The first category includes those errors caused by simple mistakes
  54. such as misspelling a variable or procedure name.  Trying to call a
  55. subprogram that doesn't exist will be immediately obvious, because BASIC
  56. gives you an error message before the program can be run.  But an incorrect
  57. variable name will return the wrong results with no warning.
  58.      Passing the wrong number of arguments to a procedure may or may not be
  59. reported, depending on whether the routine has been declared.  Assembly
  60. language routines in a Quick Library can be particularly pesky in this
  61. regard.  Although BASIC automatically generates a DECLARE statement for
  62. BASIC subprograms and functions you have loaded in source form, it does not
  63. do this for routines in a Quick Library.  If you call an assembly language
  64. routine incorrectly, you will probably crash the PC.  However, it is also
  65. possible to corrupt string memory and not know it.  Worse, a "String space
  66. corrupt" error is often not reported until much later in the program.  If
  67. you run the short program below in the QuickBASIC 4.5 editor, it will
  68. appear to operate correctly.
  69.  
  70.  
  71. X$ = SPACE$(1000)       'create a string
  72. POKE SADD(X$) - 2, 100  'corrupt string memory
  73. PRINT "Testing"
  74. X% = 1
  75. PRINT "More testing"
  76. X% = 2
  77. PRINT "Yet more testing"
  78. X% = 3
  79.  
  80.  
  81. Here, the POKE statement is overwriting the back pointer that belongs to
  82. X$, which is one type of string corruption that can occur.  But QuickBASIC
  83. doesn't know that this has happened, because it has no reason to check the
  84. integrity of its string memory until another string assignment is made. 
  85. However, adding the statement PRINT FRE("") anywhere after the POKE command
  86. causes BASIC to check string memory, and report the error.  Even if your
  87. program does not use POKE, calling a procedure incorrectly can cause it to
  88. overwrite memory in this fashion.
  89.      Another simple error is inadvertently using the same variable name
  90. twice, or omitting a type declaration character from a variable name.  For
  91. example, if you are using a variable named Bytes& to track how many bytes
  92. of a file have been read, accidentally using Bytes later on will give the
  93. wrong results.  If a DEFINT statement is in effect, then Bytes will be an
  94. integer variable.  Otherwise, it will be single precision which is also
  95. incorrect.  Unless you use the DIM...AS statement to declare a variable
  96. explicitly, BASIC lets you have different variables with the same name. 
  97. That is, Var%, Var!, and Var# can all coexist in the same program, and each
  98. is a unique variable.
  99.      Similarly, using the wrong variable entirely will cause your program
  100. to operate incorrectly, and again with no error message displayed.  More
  101. than once I have had a program with one FOR loop nested within another, and
  102. used the outer loop counter variable when I meant to use the inner one.
  103.      Another common situation is caused by changing the name of a variable
  104. during the course of writing a program.  For example, you may have a
  105. variable named BPtr that tracks where you are reading within a buffer.  If
  106. you later decide to change that name to BufPointer because it is more
  107. meaningful, you must also remember to change all occurrences of the name. 
  108. Of course, BASIC's search and replace feature minimizes that problem.  More
  109. important, though, you must make a mental note to use the new name as you
  110. continue to develop the program.
  111.      Forgetting to declare a function can also lead to incorrect results
  112. that produce no warning.  If an integer function is not declared, then
  113. BASIC will dimension an array with that name if the function expects a
  114. numeric argument.  When BASIC encounters the statement X = FuncName%(Y%) it
  115. assumes that FuncName% is an integer array, and create an array containing
  116. the default 11 elements.  In this case X will be assigned a value of zero,
  117. or you will receive a "Subscript out of range" error if Y% is not between 0
  118. and 11.  I once observed an unexplainable "Out of string space" error that
  119. was caused by the statement Size = ScreenSize%(ULRow, ULCol, LRRow, LRCol). 
  120. ScreenSize% was a function present in a Quick Library, but without a
  121. DECLARE statement BASIC created a 4-dimensional integer array.
  122.  
  123.  
  124. LOGIC ERRORS
  125. ============
  126.  
  127. The second cause of bugs is logic errors, and these include adding when you
  128. meant to subtract, or using the wrong variable altogether.  Programs that
  129. manipulate pointers (variables that hold the addresses of other variables)
  130. are particularly prone to errors in logic.  Another common logic error is
  131. forgetting to trim the leading or trailing blanks from a file or directory
  132. name before using it.  If the operator enters "  c:\thisfile.dat" and you
  133. try to open that file, BASIC will report a "Bad file name" error.
  134.      Another cause of logic errors is failing to consider all of the things
  135. a user may enter.  An inexperienced operator is likely to enter data that
  136. you as the programmer would never consider, or select menu items in an
  137. order that makes no sense.  Indeed, never underestimate the value of beta
  138. testers.  After you have exhausted all of the possibilities you can think
  139. of, give the program to a 4 year old child, and ask him or her to try it
  140. while you watch.  Your uncle Ernie would be a good beta tester too, and the
  141. less he knows about your program, the more valuable his contribution will
  142. be.  People who know absolutely nothing about computers have an uncanny
  143. knack for creating "Illegal function call" errors in a program that you
  144. just know is perfect.
  145.      Similarly, you must consider all of the possible error conditions that
  146. could happen in a program.  In an error handler that has a CASE statement
  147. for each possibility you anticipate, also include a CASE ELSE clause for
  148. those you haven't thought of.  The short listing that follows shows a
  149. typical error handler that incorporates this added safety measure.
  150.  
  151.  
  152. ON ERROR GOTO HandleErr
  153.   ...
  154.   ...
  155. HandleErr:
  156.   SELECT CASE ERR
  157.     CASE 7, 14
  158.       PRINT "Out of memory"
  159.     CASE 24, 25, 27
  160.       PRINT "Fix the printer"
  161.     CASE 53
  162.       PRINT "File not found"
  163.     CASE ELSE
  164.       PRINT "Error number"; ERR
  165.   END SELECT
  166.   ...
  167.   ...
  168.  
  169.  
  170. The CASE ELSE clause lets you accommodate any possibility, and your user
  171. can then at least report to you what the error number was.  This simple
  172. example doesn't include all of the possibilities, but you can certainly see
  173. the general concept.
  174.      Another common logic error is using the same file number twice.  When
  175. a file has been opened as #1, that number remains in use until the file is
  176. closed.  This can be problematical when writing reusable modules, since
  177. there is no way to know which files may be in use by the main program. 
  178. Some programmers use #99 or another unlikely number in a routine that will
  179. be reused in many programs.  But even that approach is flawed, because you
  180. have to remember which numbers are used by which routines.
  181.      BASIC's FREEFILE function is intended to solve this problem, and it
  182. returns the next available file number.  Be sure to save the results
  183. FREEFILE returns, however, since the value will change as soon as the next
  184. file is opened.  The code below shows both the wrong and right ways to use
  185. FREEFILE.
  186.  
  187.  
  188. Wrong:
  189.  
  190.   OPEN "accounts.dat" FOR INPUT AS #FREEFILE
  191.   INPUT #FREEFILE, X$    'FREEFILE has changed! 
  192.   CLOSE #FREEFILE
  193.  
  194.  
  195. Right:
  196.  
  197.   FileNum = FREEFILE    'get and save the number
  198.   OPEN "accounts.dat" FOR INPUT AS #FileNum
  199.   INPUT #FileNum, X$
  200.   CLOSE #FileNum
  201.  
  202.  
  203. In the first example if FREEFILE returns, say, a value of 2, then it will
  204. return 3 at the INPUT statement which is of course incorrect.  Therefore,
  205. you must save the value FREEFILE returns, and use that for all subsequent
  206. file accesses.  This situation also occurs with INKEY$, because once a
  207. character has been returned it is no longer available unless you saved it.
  208.      Two other frequent problems are attempting to use LSET to assign
  209. characters into a string that does not exist, and failing to clear a
  210. counter variable within a static subprogram or function.  The second
  211. problem can be especially frustrating, because the routine will work
  212. correctly the first time it is invoked.  In the function below, a counter
  213. returns the number of embedded control characters it finds in a string.
  214.  
  215.  
  216. FUNCTION CtrlCount%(Work$) STATIC
  217.  
  218.   FOR X% = 1 TO LEN(Work$)
  219.     IF ASC(MID$(Work$, X%, 1)) < 32 THEN
  220.       Count% = Count% + 1
  221.     END IF
  222.   NEXT
  223.  
  224.   CtrlCount% = Count%    'return the count
  225.  
  226. END FUNCTION
  227.  
  228.  
  229. The problem here is that Count% retains its value between function
  230. invocations.  Therefore, each time CtrlCount% is used it will return ever
  231. higher values.  One solution is to add the statement Count% = 0 at the
  232. beginning of the function.  Another is to omit the STATIC option from the
  233. function definition.
  234.  
  235.  
  236. UNDERSTANDING BASIC'S QUIRKS
  237.  
  238. The third type of error is caused by not understanding some of BASIC's
  239. finer points and quirks.  For example, some people do not realize that
  240. omitting the third argument from MID$ causes it to return all of the
  241. remaining characters in a string.  To see if a drive letter was given as
  242. part of a file name and if so extract it, you might use a statement such as
  243. IF MID$(FileName$, 2) = ":" THEN Drive$ = LEFT$(FileName$, 1).  But since
  244. the number of characters was not specified to MID$, it returned all but the
  245. first character in the string.  Unless the string was a drive letter and
  246. colon only ("C:"), the test for a colon could never work.  The solution, of
  247. course, is to use MID$(FileName$, 2, 1).
  248.      Another instance in which an intimate knowledge of BASIC's
  249. idiosyncracies comes into play can affect the earlier example of a file
  250. name that contains leading blanks.  Most programmers do not use INPUT to
  251. accept information, unless the program is very simple and it will be used
  252. only occasionally.  However, asking for a file name with INPUT is one way
  253. to avoid that problem, because INPUT strips all leading and trailing blank
  254. spaces, as well as CHR$(9) tab characters.  The more useful LINE INPUT, on
  255. the other hand, does not strip leading blanks and tabs.  Most programmers
  256. would never be so foolish as to enter a file name with leading blanks.  So
  257. this is yet another situation where it is important to consider all of the
  258. possibilities.
  259.      It is also possible to crash a program by using the ASC function when
  260. the string might be null.  Again, *you* would never press Enter alone in
  261. response to a prompt for a file name or other mandatory information, but
  262. someone else might.
  263.      Another BASIC quirk is caused by rounding errors.  As you saw in
  264. Chapter 2, adding or multiplying many numbers in succession can produce
  265. results that are not precisely correct.  Instead of checking to see if a
  266. value is zero, it is often better to compare it to a very small number. 
  267. That is, instead of IF Value# = 0 you would use IF Value# < .000001 or IF
  268. Value# < .000001 AND Value# > -.000001 or something similar.  Also, some
  269. numbers simply cannot be represented at all.  If you try to enter the
  270. statement X# = .00000000001 in the QuickBASIC 4.5 editor, the value will be
  271. converted to 9.999999999999999D-12 as soon as you press Enter.
  272.      Although not technically a BASIC quirk, many programmers forget that
  273. variables within a DEF FN function are by default global.  Unless you
  274. include an explicit STATIC statement listing each variable that is to be
  275. local to the function, it is likely that an unexpected change will be made
  276. to a variable in the main program.
  277.      Some programming situations require that you obtain the address of a
  278. string variable using SADD.  However, SADD is not legal for use with a
  279. fixed-length string or the string portion of a TYPE variable.  More
  280. important, when using BASIC PDS far strings you must also remember to use
  281. SSEG to get the string's data segment.  Using VARSEG will not create an
  282. error; however, the program will not work correctly.
  283.      Related to that, it is important to remember that strings and dynamic
  284. arrays move around in memory--often at unexpected times.  The program below
  285. appends a zero character to one string for each zero that is found in
  286. another string.  Since BASIC may move Work$ during the course of assigning
  287. Zero$, this code will fail eventually:
  288.  
  289.  
  290. Address = SADD(Work$)
  291. FOR Y = Address TO Address + LEN(Work$) - 1
  292.   IF PEEK(Y) = 48 THEN Zero$ = Zero$ + "0"
  293. NEXT
  294.  
  295.  
  296. Another particularly insidious bug can result if you inadvertently add
  297. parentheses around a variable that is passed to a subprogram or function. 
  298. In the example below, a subprogram that intentionally modifies a parameter
  299. has been declared and is then called without the CALL keyword.
  300.  
  301.  
  302. DECLARE SUB Square(Param%)
  303. Square (Value%)
  304.  
  305. SUB Square(Value%) STATIC
  306.   Value% = Value% * Value%
  307. END SUB
  308.  
  309.  
  310. Because of the unnecessary and incorrect use of parentheses, a copy of the
  311. argument is sent to Square instead of the argument itself, with the result
  312. that Value% is never actually changed.  The fix is to either remove the
  313. parentheses, or add the word CALL.  Another, related issue is placing a
  314. DEFINT after DECLARE statements.  In the example below, the parameters X,
  315. Y, and Z are assumed by BASIC to be single precision, even though this is
  316. clearly not what was intended.
  317.  
  318.  
  319. DECLARE SUB (X, Y, Z)  'X, Y, and Z are singles!
  320. DEFINT A-Z
  321.  .
  322.  .
  323.  
  324.  
  325. The final issue I want to address here is potential overflow errors.  The
  326. statement IF IntVar% * 14 > 1000000 can never be true, because BASIC
  327. performs integer math assuming an integer range only.  Unless you compile
  328. your program using the /d debug option, the error will be unreported in a
  329. compiled program.  If this statement is executed within the QB environment,
  330. BASIC will report an overflow error, even though the instruction certainly
  331. appears to be legal.  But since integer math assumes an integer result, the
  332. product of IntVar% times 14 will overflow the range of integer values if
  333. IntVar% is greater than 2,340.
  334.      One solution is to use a long integer for IntVar, and BASIC will then
  335. use the range of long integers for the comparison.  Using a long integer
  336. wastes memory, however, and calculations on long integers are slower and
  337. require more code to implement.  A much better solution is to use CLNG
  338. (Convert to Long), which tells BASIC to assume a long integer result.
  339.      The statement IF CLNG(IntVar%) * 14 > 1000000 will create a long
  340. integer version of IntVar%, and then multiply the result times 14 and use
  341. that for the subsequent comparison.  Unlike the copies that BASIC makes
  342. which steal DGROUP memory, the long integer conversion in this instance is
  343. handled within the CPU's registers.  CLNG when used this way is really just
  344. a compiler directive, as opposed to a called library routine.  Another
  345. solution is to add an ampersand after the constant 14, thus: IF IntVar% *
  346. 14& > 1000000.  Again, no additional DGROUP memory is used to handle 14 as
  347. a long integer value.
  348.      Another interesting use of CLNG and CINT--unrelated to debugging but
  349. worth mentioning none the less--is to reduce the size of comparison code. 
  350. When you use a statement such as IF X% > VAL(Some$), a floating point
  351. comparison is performed even if Some$ holds an integer value.  By replacing
  352. that example with IF X% > CINT(VAL(Some$)) 6 bytes of code can be saved. 
  353. The CINT tells BASIC that it will not have to perform any floating point
  354. rounding when it compares the two values.
  355.  
  356.  
  357. DEBUGGING AND TESTING TECHNIQUES
  358. ================================
  359.  
  360. When you are developing a large application that is comprised of many
  361. individual modules, there are several useful debugging techniques you can
  362. employ.  One is to create short test-bed programs that exercise each
  363. subprogram and function.  Finding an error in a complex program with many
  364. interdependencies between subroutines can be a tedious prospect at best. 
  365. If you instead create a small program whose sole purpose is to test a
  366. particular subprogram, you will be better able to focus on just that
  367. routine.
  368.      Another useful technique for detecting and preventing sporadic errors
  369. is to test your code on "boundary conditions".  If you have a routine that
  370. reads and process a file in 4K (4096 byte) increments, test it with a file
  371. that is exactly 4096 bytes long, as well as with other test files that are
  372. 4095 and 4097 bytes long.
  373.      Perhaps nothing is more frustrating than having a program fail with
  374. the message "xxx at line No line number".  This message is a throw-back to
  375. the days when all BASIC programs had to use line numbers.  Now that line
  376. numbers are not required in modern compiled BASIC, most programmers do not
  377. use them, opting instead for more descriptive line labels when labels are
  378. needed at all.  When an error does occur and the program has been compiled
  379. with /d, BASIC reports the number of the nearest numbered line preceding
  380. the line in which the error occurred.
  381.      A good solution to track down the cause of such errors is to use a
  382. variant on a hardware debugging technique known as the "cut in half"
  383. method.  In a complex electronic circuit that does not work, using this
  384. technique means that the circuit is first checked at its mid-point for the
  385. correct signal.  If the circuit tests correctly at that point, then the
  386. error is in the second half.  Therefore, the test engineer would "cut in
  387. half" again, and test at a point halfway between the middle and the end. 
  388. If the test fails there, then the problem must lie between the middle of
  389. the circuit and that point.
  390.      In a purely software situation, you would add a line number to a line
  391. that falls approximately half-way through the program.  If that number is
  392. reported, then the problem is occurring in the second half of the program. 
  393. An enhancement to this technique that I recommend is to add, say, ten line
  394. numbers in evenly spaced increments throughout the program.  This will let
  395. you quickly isolate the problem to a much smaller portion of the program.
  396.      Besides the line number (or lack of line number) that BASIC reports,
  397. the segment and address at which the error occurred is also reported.  This
  398. is information is frankly useless in a purely BASIC environment.  You must
  399. either use CodeView to identify the line that is associated with the error,
  400. or view the assembly language output that BC can optionally generate. 
  401. These will be described in the section on advanced debugging later in this
  402. chapter.
  403.      Finally, it is important to point out that you should never use ON
  404. ERROR while a program is being developed.  ON ERROR can hide programming
  405. errors that you need to know about.  As an example, a LOCATE statement with
  406. incorrect values will generate an "Illegal function call" error.  But if ON
  407. ERROR is in effect and your program uses RESUME NEXT for errors it is not
  408. expecting, you may never even know that an error occurred.  If you run the
  409. complete program below you can see that there is no indication that an
  410. error occurred at the obviously illegal LOCATE statement.
  411.  
  412.  
  413. CLS
  414. ON ERROR GOTO HandleErr
  415. LOCATE 100, -90
  416. PRINT "My program seems to work fine."
  417. END
  418.  
  419. HandleErr:
  420. RESUME NEXT
  421.  
  422.  
  423. USING THE QB AND QBX EDITING ENVIRONMENTS
  424.  
  425. The single most powerful debugging feature that is available to you is the
  426. BASIC editing environment.  More than just an editor that you can use to
  427. enter program statements, the QB environment is exactly that: a complete
  428. editing environment for developing and testing BASIC programs.  The BASIC
  429. editor lets you enter program statements, single-step through a program,
  430. examine variable values, and much more.  Besides being able to execute
  431. commands singly and in sequence, you can also trace into subroutines and
  432. functions, and even run your program in reverse.
  433.      The primary advantage of using the QB environment instead of a
  434. separate editor is the enhanced debugging capabilities.  In most high-level
  435. languages, you first write a program using an editor, and then compile and
  436. run it to see if it works correctly.  If an error occurs, you must start
  437. the editor again, load your program, and study the code to see what went
  438. wrong.  In contrast, QB lets you run your program at the same time it is
  439. being edited.  You can even modify the program while it is running and then
  440. resume execution, view and change variable values, and change the order in
  441. which statements are executed.
  442.      Further, BASIC can be instructed to stop and return to the edit mode
  443. when the program reaches a certain statement, or when a particular logical
  444. condition becomes true.  For example, you can tell BASIC to halt the
  445. program when a variable takes on a specified value.  These are extremely
  446. powerful debugging tools which have no equal in any other language.  In the
  447. sections that follow, I will describe each of these capabilities in detail.
  448.  
  449.      
  450. STEP AND TRACE DEBUGGING
  451.  
  452. Early versions of Microsoft BASIC offered a very primitive trace capability
  453. that displayed the line numbers of the currently executing statements. 
  454. Although this was better than nothing, interpreting a blur of line numbers
  455. flashing by on the screen required a lot of mental effort.  When Microsoft
  456. introduced QuickBASIC version 3.0 they added greatly improved debugging in
  457. the form of a step and trace feature.  To activate step and trace you would
  458. enter a STOP statement at a selected point in the source code.  When the
  459. program reached that point you could then execute each statement in
  460. sequence by pressing a function key.  QuickBASIC 3 also provided the
  461. ability to display continuously the value of a single variable in a window
  462. at the top of the screen.
  463.      QuickBASIC 4.0 offered an improved version of this feature, using
  464. additional function keys to control how a program proceeds.  This method
  465. has been continued with little change through current versions of
  466. QuickBASIC and BASIC PDS.  Of course, the primary reason you would want to
  467. step through a program one statement at a time is to determine why it is
  468. not working.  For example, if you have code that opens a file for output
  469. but the file is never created, you would step through that portion of the
  470. code to see which statements are being executed and which are not.  In
  471. particular, stepping through a program lets you see which path an IF or
  472. CASE test is taking.
  473.      Two function keys are used to single-step through a program, and four
  474. additional options are available to assist program debugging.  Each time
  475. the F10 key is pressed, the current statement is executed and the program
  476. advances to the next statement.  If you have just loaded the program being
  477. tested, you will press F10 once to get to the first instruction.  Pressing
  478. F10 again executes that statement, and continues to the next one.  If the
  479. current statement is related to screen activity, the screen is switched
  480. momentarily to display the program's output rather than the source code. 
  481. The screen is also switched during a CALL statement or function invocation,
  482. in case that routine performs screen output.  You can optionally toggle
  483. between viewing the output and edit screens manually by pressing F4.
  484.      In some cases you may want to treat a subroutine as a single
  485. statement, which is what F10 does.  That is, CALL MySub is handled as
  486. single statement, and all of the statements within the routine are executed
  487. as one operation.  In other cases, however, you may need to trace into a
  488. subprogram, GOSUB routine, DEF FN, or function, to step through its
  489. statements as well.  This is what F8 is for.  When F8 is pressed at a CALL
  490. or GOSUB statement or function invocation, BASIC traces into the procedure
  491. and lets you watch as it executes each statement individually.
  492.      Two additional capabilities let you navigate a program more quickly. 
  493. Pressing F7 tells BASIC to execute all of the statements up to the current
  494. cursor location.  This way, you are spared from having to watch a long
  495. sequences of commands that you know are working correctly.  For example,
  496. stepping through a FOR/NEXT loop that initializes 1000 elements in an array
  497. is usually pointless.  Therefore, when you reach that spot in the program
  498. you would manually move the cursor to the statement following the NEXT, and
  499. press F7.
  500.      It is also possible to force execution to a particular point in the
  501. program using the "Set next statement" option of the Debug menu.  Unlike
  502. F7, though, the statements that precede the selected line will not be
  503. executed.  Therefore, this option is equivalent to adding a temporary GOTO
  504. to the program, causing it to jump to the specified line.
  505.      One of the most powerful features of the BASIC editor is that you can
  506. actually modify your program, then resume execution.  In earlier versions
  507. of QuickBASIC, making even the slightest change to a program--even if only
  508. to a single comment--the entire program would have to be recompiled.  BASIC
  509. can now preserve variable values and indeed the entire program state during
  510. most types of editing operations.
  511.      The last important step operation I want to mention now is the History
  512. feature.  This too must be selected from a menu, and using it will slow
  513. your program's operation considerably.  When the History option is selected
  514. from the Debug menu, BASIC remembers the last 25 program statements, and
  515. lets you step through your program in reverse.  For example, if a variable
  516. has taken on an incorrect value, you can walk backwards through the program
  517. to see what statements caused that to happen.  Where F8 steps forward
  518. through your program, Shift-F8 instead steps backward.
  519.  
  520.  
  521. WATCH VARIABLES AND BREAK POINTS
  522.  
  523. As powerful as BASIC's single-step feature is, it is only half of the
  524. story.  Equally important is the Watch capability that lets you view a
  525. program's variables in real time.  One or more variables may be placed into
  526. a special Watch window at the top of the editing screen, and their values
  527. will be displayed and updated after each statement is executed.  Between
  528. the Step and Watch features, you can observe all aspects of your program's
  529. operation as it is executing.
  530.      Besides watching variable values, you can also monitor complex
  531. expressions and function results.  For example, you could watch the value
  532. of X% * Y% + Z%, ASC(Work$), or the result of a function such as
  533. StrFunction$(Array$(), Count%).  Because each variable or expression is
  534. updated after every program statement, your program will run more slowly
  535. when many items are displayed in the watch window.  However, this is seldom
  536. a problem in a debugging situation, and the ability to see precisely what
  537. is happening far outweighs the minor speed penalty.
  538.      Being able to watch the results of expressions as well as simple
  539. variables offers some useful and interesting techniques.  As an example,
  540. suppose you are watching a string variable named Buffer$.  If Buffer$ is
  541. very long, you can use LEFT$ or MID$ to watch just a portion of the string:
  542. MID$(Buffer$, CurPointer%, 70).  This expression displays the 70-character
  543. portion of Buffer$ that is currently pointed to by CurPointer% (assuming,
  544. of course, you are using variables with those names).
  545.      Likewise, if you are observing a string but nothing is showing in the
  546. watch window, you could watch "{" + Work$ + "}".  This displays "{}" if the
  547. string is null, and shows if there are leading or trailing blanks or
  548. CHR$(0) bytes.  Adding braces also lets you see if the string contains
  549. characters that begin past the edge of the visible window.
  550.      One particularly powerful use of BASIC's Watch capability is related
  551. to the fact that all of the expressions are evaluated anew at each
  552. statement.  Earlier I mentioned how insidious "String space corrupt" errors
  553. can be, because BASIC checks the integrity of its string memory only when a
  554. string is being assigned.  Therefore, watching the expression FRE(Any$)
  555. tells BASIC to evaluate string memory after every source line.  Thus, as
  556. soon as string memory is corrupted it will be immediately reported.  This
  557. technique can be extended to identify a "Far heap corrupt" error as well,
  558. by watching the expression FRE(-1).
  559.      Besides the Step and Watch capabilities, there are two additional
  560. features you should understand: Break Points and Watch Points.  When a
  561. program is very large and complex, it becomes impractical to step and trace
  562. through every statement.  Also, in some cases you may not know at which
  563. statement an error is occurring.
  564.      Pressing F9 sets up a Break Point which tells BASIC to halt when it
  565. reaches that point in the program, regardless of how it arrived there.  You
  566. can have multiple break points, and the program will run normally until the
  567. specified statement is about to be executed.  Simply place the cursor on
  568. the line at which the program is to stop, and press F9.  That line will be
  569. highlighted to show that it is currently a Break Point.  Pressing F9 again
  570. removes the Break Point.
  571.      A Watch Point tells BASIC to execute the program, until a certain
  572. condition becomes true.  Some examples of Watch Points are X% = 100,
  573. ABS(Total#) > 1000, and FRE("") < 1000.  In the first example you are
  574. telling BASIC to stop the program and return to the editor when X% equals
  575. 100.  The second example will stop the program when the absolute value of
  576. Total# exceeds 1000, and the third halts it when there are less than 1000
  577. bytes of string space remaining.
  578.      Considered together, these debugging features are extremely powerful. 
  579. You can tell BASIC, in effect, "Run until the value of Count% hits 14; then
  580. stop the program, and let me walk backwards through the program to see how
  581. that happened."
  582.  
  583.  
  584. USING /D TO DETECT ERRORS
  585.  
  586. Another very powerful debugging solution at your disposal is to compile
  587. your program with the /d debug option.  When creating an .EXE file in the
  588. BASIC environment from the Run menu, you would select the "Produce debug
  589. code" option.  Compiling with /d tells BC to add three important safeguards
  590. to the code it generates.  Some of these debugging issues were described in
  591. Chapter 1, but they deserve elaboration here.
  592.      The first code addition is a call to a central event handler prior to
  593. every BASIC program statement, to detect if Ctrl-Break was pressed. 
  594. Normally, a compiled BASIC program is immune from pressing Ctrl-Break and
  595. Ctrl-C, unless the program is processing an INPUT statement.  BASIC adds
  596. break checking to let you get out of an endless loop or other similar
  597. situation, without having to reboot your computer.
  598.      The second addition is an overflow test following each integer and
  599. long integer addition, subtraction, and multiplication, to detect results
  600. that exceed the range of legal values.  If you have a statement such as X%
  601. = Y% * Z% and the result after multiplying is greater than 32767, the
  602. overflow test will detect that and produce an error message.  Otherwise, X%
  603. would be assigned an erroneous value and your program would have no way to
  604. detect it.  Floating point operations do not need any additional testing,
  605. because overflows are detected and reported whether or not /d is used.
  606.      The last additional code that BASIC adds when /d is used is array
  607. element bounds checking.  If you have dimensioned an array and attempt to
  608. assign an element that doesn't exist, a compiled BASIC program will
  609. normally ignore the error.  For example, if an array has been dimensioned
  610. using DIM Array%(1 TO 100) and you then have the statement Array%(200) =
  611. 12, BASIC will store the value 12 at what would have been the 200th
  612. element.  This can lead to disastrous consequences such as overwriting an
  613. element in another array, or corrupting string memory.  When /d is used
  614. BASIC adds additional code to check every array element referenced, and
  615. reports an error if that element does not exist.
  616.      Because of the added checking for overflow errors and illegal element
  617. numbers, a program compiled with /d will be larger and run more slowly than
  618. one in which /d is not used.  Therefore, you should not release a program
  619. for general use that has been compiled with the debug option.  One
  620. exception worth noting is that QuickBASIC versions 4.0 and 4.5 contain a
  621. bug that generates incorrect code for certain long integer array
  622. operations.  The only solution when that happens is to use /d.  This way,
  623. the routine that calculates element addresses and checks for illegal
  624. element numbers is used, rather than the incorrect in-line code that BC
  625. produces directly.
  626.      You could also compile with the /ah (huge array) switch, which uses
  627. the same routine to calculate and check array element addresses.  Using /ah
  628. has an advantage over /d in this case, because your program will not be
  629. halted if Ctrl-Break is pressed. Using /ah also avoids the extra code and
  630. time to check for overflow errors.  However, /ah affects dynamic arrays
  631. only, and errors with static arrays will not be prevented.
  632.      When a program is run in the BASIC editor, the same protection that /d
  633. provides is employed.  This added debug testing within the editor is one
  634. more contributor to its slowness when compared to a fully compiled program.
  635.  
  636.  
  637. ADVANCED DEBUGGING
  638.  
  639. Although being able to step through your program and watch its variables in
  640. the BASIC editing environment is very powerful, there are still some
  641. limitations inherent in that process.  For example, it is possible that a
  642. program will work perfectly in the editor, but not when it has been
  643. compiled to an .EXE program.  Microsoft has tried to make the BASIC editor
  644. as compatible with BC as possible, but the editor is an interpreter and not
  645. a true compiler.  There are bound to be some differences in how the program
  646. runs.  Another limitation is that some programs are just too large to be
  647. run within the editor.  Finally, if you receive an error message from an
  648. executable program that lists only a segment and address, there is no way
  649. to determine where the error occurred using the editor.
  650.      In these cases you will need to work with the actual compiled program. 
  651. To relate an error address to the original BASIC source statement you must
  652. be able to see the assembly language code that BC generates, along with the
  653. original BASIC source.  One way to do this is with the Microsoft CodeView
  654. debugger.  CodeView comes with BASIC PDS [and VB/DOS Professional Edition]
  655. as well as with Microsoft's Macro Assembler.  CodeView provides a debugging
  656. environment that is similar to the QB editor, except it is intended for
  657. tracing through a program that has already been compiled.
  658.      Another way is to instruct BC to generate an assembly language source
  659. listing as it compiles your program.  This listing shows a mix of BASIC
  660. source statements and the resultant assembly language code and addresses. 
  661. However, the listing is not as clear or easy to follow as the display that
  662. CodeView presents.  But if you do not have CodeView, this is your only
  663. choice.  I will describe this method first.
  664.  
  665.  
  666. CREATING AN ASSEMBLY LANGUAGE SOURCE LISTING
  667.  
  668. To create an assembly language list file you use the compiler's /a switch,
  669. and then specify a list file name.  The syntax is shown below, followed by
  670. a sample list file that is generated.
  671. You enter this:
  672.  
  673. bc program /a [/other options] , , listfile;
  674.  
  675.  
  676. LISTFILE.LST contains this:
  677.                                        PAGE   1
  678.                                        25 June 91
  679.                                        14:28:08
  680.   Microsoft (R) QuickBASIC Compiler Version 4.50
  681.  
  682. Offset Data  Source Line
  683.  
  684.  0030  0006  CLS
  685.  0030  0006  INPUT Count%
  686.  0030   **     I00002: mov   ax,0FFFFh
  687.  0033   **             push  ax
  688.  0034   **             call  B$SCLS
  689.  0039   **             mov   ax,offset <const>
  690.  003C   **             push  ax
  691.  003D   **             call  0000h
  692.  0040   **             pop   ax
  693.  0041   **             add   ax,000Dh
  694.  0044   **             push  cs
  695.  0045   **             push  ax
  696.  0046   **             call  B$INPP
  697.  004B   **             jmp   $+04h
  698.  004D   **             dw    0002h
  699.  004F   **             db    00h
  700.  0050   **             db    02h
  701.  0051   **             mov   bx,offset COUNT%
  702.  0054   **             push  ds
  703.  0055   **             pop   es
  704.  0056   **             push  es
  705.  0057   **             push  bx
  706.  0058   **             call  B$RDI2
  707.  005D  0008  IF Count% < 100 THEN
  708.  005D  0008     Count% = 100
  709.  005D  0008  END IF
  710.  005D   **             call  B$PEOS
  711.  0062   **             cmp   word ptr COUNT%,64h
  712.  0067   **             jl    $+03h
  713.  0069   **             jmp   I00003
  714.  006C   **             mov   COUNT%,0064h
  715.  0072  0008  PRINT Count%
  716.  0072  0008  END
  717.  0072  0008
  718.  0072  0008
  719.  0072   **     I00003: push  COUNT%
  720.  0076   **             call  B$PEI2
  721.  007B   **             call  B$CEND
  722.  0080   **             call  B$CENP
  723.  0085  0008
  724.  
  725. 43981 Bytes Available
  726. 43643 Bytes Free
  727.  
  728.     0 Warning Error(s)
  729.     0 Severe  Error(s)
  730. Here, the list file shows the original BASIC source code, as well as the
  731. generated assembly language instructions.  The column at the left holds the
  732. code addresses, and these correspond to the addresses that BASIC displays
  733. when a program crashes with an error message.  Unfortunately, several BASIC
  734. statements are grouped together, so it is not immediately apparent which
  735. address goes with which source statement.  For example, after the BASIC
  736. statement INPUT Count%, the earlier assembly language instructions that
  737. clear the screen are shown.  Similarly, the call to B$PEOS is actually part
  738. of the INPUT code, although it is listed following the IF test.
  739.      When BASIC displays an error message and ends your program by
  740. displaying a segmented address, only the address portion is meaningful. 
  741. The segment in which a program is running will depend on many factors,
  742. including the DOS version (and thus its size), the FILES= and BUFFERS=
  743. values specified in CONFIG.SYS, and whether TSR programs and device drivers
  744. are loaded.  Each of these factors cause the program to be loaded at a
  745. higher segment, although the addresses within that segment never change. 
  746. Also, in a multi-module program, a different segment is used for each
  747. module's source file.  Therefore, if the message is "Illegal function call
  748. in module XYZ at address 3456:1234", you would compile XYZ.BAS to create a
  749. list file instead of the main program.  The code in the vicinity of address
  750. 1234 will be where the error occurred.
  751.  
  752.  
  753. USING MICROSOFT CODEVIEW
  754.  
  755. Although compiling with the /a switch lets you view the assembly language
  756. code that BASIC creates, there is little you can actually do with that
  757. information.  CodeView is a much more powerful debugging tool, and it lets
  758. you step through an .EXE file as it is running.  This lets you follow the
  759. compiled program's execution path, and also view its assembly language
  760. instructions.  Further, CodeView can trace into BASIC's library routines,
  761. as well as calls to C or assembly language routines that you have written.
  762.      CodeView can also be used to see how many bytes of code are generated
  763. for each BASIC statement.  This is a good way to compare the relative
  764. efficiency of different programming methods, to see which ones produce less
  765. code.  It is important to understand that the size of the assembly language
  766. code generated for a given BASIC statement is a combination of two factors:
  767. the number of bytes the compiler generates for each occurrence of the
  768. statement, and the size of the called routine within BASIC's runtime
  769. library.  Of course, the called routine is added to your program only once. 
  770. However, the code that sets up and calls the routine is added each time the
  771. statement is encountered.
  772.      Compiling a program for use with CodeView is very simple, and merely
  773. requires the addition of special compiler and linker option switches.  Note
  774. that you cannot compile a program for CodeView from within the QuickBASIC
  775. editor; you must compile and link manually from the DOS command line, as
  776. shown below.  Also notice that the BASIC program must be saved as ASCII
  777. text, and not with the special "Fast Load" method that QB optionally uses.
  778.  
  779.  
  780. bc program /zi [/other options];
  781. link program /co [/other options];
  782. cv program
  783.  
  784.  
  785. The /zi option tells BC to write additional information into the object
  786. file, which is used by LINK and CodeView to relate each line of BASIC
  787. source code to its resultant assembly code.  The more meaningfully named
  788. /co switch is required so LINK will know to do likewise.  You may be
  789. interested to know that /zi is named after Microsoft legend Mark
  790. Zibikowski, whose initials (MZ) also appear as the first two bytes in every
  791. DOS .EXE file.
  792.      Once the program has been compiled and linked, start CodeView by
  793. entering CV followed by the file's first name (that is, without the .BAS or
  794. .EXE extension).  You will then be presented with a screen very similar to
  795. that of the QB editor.  Most versions of CodeView initially show the BASIC
  796. source code.  In other versions, you must press Alt-R-R to "restart" the
  797. program and bring it to the first source line.  I should point out that
  798. CodeView is a quirky program, and it is often referred to as the program
  799. that people "love to hate".  It has some glaring omissions, many aspects of
  800. its interface are inconsistent and downright obnoxious, and I personally
  801. would be lost without it.
  802.      When the BASIC source is displayed, you may press F4, F7, F8, and F10,
  803. which perform the same functions as their BASIC editor counterparts.  One
  804. important difference, however, is that you may also press F3 to show a mix
  805. of BASIC and assembly language code.  Stepping through the program with F8
  806. and F10 will execute either a single BASIC statement or a single assembler
  807. command, depending on the context.  That is, if you are in the BASIC view
  808. mode, then you will step through the BASIC code.  If the assembly language
  809. code is being displayed, then you will step through that instead.
  810.      Figure 4-1 [not available here, sorry] shows a screen snapshot of a
  811. short sample program as displayed by CodeView when it is first started in
  812. the BASIC view mode.  Figure 4-2 [also unavailable] shows the same program
  813. after pressing F10 to execute up to the first statement, followed by F3 to
  814. view a mix of BASIC and assembly language.  This screen is in a 50-line
  815. mode to allow the entire program to be displayed.  Although it is not shown
  816. here, CodeView can continuously display the processor's registers in a
  817. small window at the right side of the screen.  The register display is
  818. alternately activated and deactivated by pressing F2.
  819.  
  820.  
  821. FIG4-1: The CodeView display when using the BASIC view mode.
  822.  
  823.  
  824. FIG4-2: The CodeView display for the same program, but using the assembly
  825. language view mode.
  826.  
  827.  
  828. Notice in Figure 4-2 that CodeView displays each BASIC statement indented
  829. and with a line number.  This lets you identify where each BASIC command
  830. starts, and also which block of assembly language code it is associated
  831. with.  The numbers at the left edge of the display show the segment and
  832. address of each instruction in hexadecimal notation.  The segment value
  833. never changes within a single program module, although the addresses
  834. increase based on the number of bytes in each assembly language
  835. instruction.  As you can see, some assembly language commands are as short
  836. as one byte, and others are as long as six.
  837.      In the first instruction, CLS, a value of -1 (FFFF hex) is passed to
  838. the CLS routine as a flag to show that no argument was given.  Had the
  839. BASIC statement been CLS 2, then a value of 2 would have been moved into AX
  840. instead.  Nine bytes of code are generated each time CLS is used, not
  841. counting the code within B$SCLS.  Besides showing the B$SCLS routine name,
  842. CodeView also shows the segment and address at which B$SCLS resides. 
  843. Knowing the routine's address is of little practical use in this situation,
  844. and it is displayed solely for informational purposes.
  845.      The INPUT statement is fairly complicated to set up, and I won't
  846. belabor what every assembly language instruction does.  But several items
  847. are worth discussing.  The first is that CodeView attempts to relate every
  848. number it encounters to a variable or procedure address.  In many cases
  849. this is confusing, because some numbers are simply that, and have no
  850. relationship to a variable or procedure address.
  851.      For example, at address 39 the assembly language command MOV AX,40 is
  852. shown as MOV AX,b$STRTAB_END+10 (0040), as if there was some significance
  853. to the fact that the value 40 is an address ten bytes past the end of an
  854. internal string table.  Likewise, two instructions later the value 40 is
  855. represented as being 31 bytes past the beginning of the B$LENDRW procedure. 
  856. Two instructions past that the value 13 (0D hex) is added to AX, and again
  857. CodeView tries to establish a significance where none exists.
  858.      In not one of these cases are the values shown related to the named
  859. address, and you should therefore treat those named labels with skepticism. 
  860. The only symbolic names that are meaningful in most cases are variable and
  861. procedure names that do not have an extra value added to them.  In the
  862. instruction MOV Word Ptr [COUNT% (0036)],b$HEAP_FIRST (0064) at address 6C,
  863. the address for Count% (36) is valid, while the value 64 named b$HEAP_FIRST
  864. is meaningless.  In this case, 64 hex represents the value 100 in the BASIC
  865. statement Count% = 100.  Whatever b$HEAP_FIRST may represent, it has no
  866. meaning here.
  867.      I suggest that you enter this short program and then step through it
  868. one statement at a time, just to get a feel for how CodeView operates.  You
  869. should also try tracing into some of the BASIC library calls, as well as
  870. into a simple subprogram or two of your own.  Again, you may use either F10
  871. or F8 to step through the code, but only F8 will trace into code that is
  872. being called.  You can also use F8 to trace into some BIOS interrupts, but
  873. you should never try to trace through a DOS interrupt (21 hex).  Many DOS
  874. services never return, or return in a non-standard manner, and a locked-up
  875. PC is the likely result.  You will not hurt anything if you do trace into a
  876. DOS interrupt, but be prepared to press Ctrl-Alt-Del.
  877.      Besides being able to view and step through the assembly language code
  878. that BASIC creates, you can also view and modify your program's data
  879. directly.  If you have pressed F2 to display the CPU's registers, CodeView
  880. will show the value currently in every memory address that is about to be
  881. accessed.  For example, if the next statement to be executed is MOV Word
  882. Ptr [COUNT%],10, CodeView will show the current contents of the variable
  883. COUNT%.
  884.      A range of memory addresses may be displayed by entering commands into
  885. the immediate window at the bottom of the screen.  When CodeView is first
  886. started, the cursor is placed at the bottom line in that window.  As with
  887. the BASIC editor, the F6 key is used to toggle between the code output and
  888. immediate windows.  Unlike the BASIC editor, however, you may type commands
  889. regardless of which window is active.
  890.      The three primary commands you will find useful are D, U, and R.  The
  891. D (Dump) command tells CodeView to display a range of memory, starting at a
  892. given address.  For example, D 0 means to show the 32 bytes that start at
  893. address 0 in the default data segment.  Likewise, D ES:100 means to start
  894. at address 100 in the segment held in the ES register.  Unfortunately,
  895. CodeView is particularly obtuse in this regard, because in some cases the
  896. numbers you enter are assumed to be decimal while in others it assumes
  897. hexadecimal.  Which is which depends on your view perspective (selected
  898. with F3), and I won't even begin to offer a reason or explain the confusing
  899. rules.  If you don't get what you expect, try adding an "&H" prefix to the
  900. number.  And if you start by using &H and CodeView reports a syntax error,
  901. then try it without the &H.
  902.      When the contents of memory are displayed, they are shown as
  903. individual bytes, rather than as integer words which is generally more
  904. useful.  In the listing below, two string constants have been displayed in
  905. response to the command D &H40.  For space reasons, the segment and address
  906. which CodeView adds to the left of each row of values are instead shown
  907. above the rows.
  908.  
  909.  
  910. >D &H40
  911.  
  912. 5676:0040
  913. 02 00 44 00 48 69 23 00 4A 00 41 42 43 44 45 46
  914. 5676:0050
  915. 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56
  916.  
  917.  
  918. As you learned in Chapter 2, BASIC near strings have a 4-byte descriptor,
  919. with the first two bytes holding the string's current length, and the
  920. second two bytes its current address.  Beginning with the first two numbers
  921. displayed, the 02 00 represents the length of a 2-character string, and the
  922. 44 00 indicates the address which is 44.  The data itself is a CHR$(&H48)
  923. followed by a CHR$(&H61) ("Hi"), and it immediately follows the string
  924. descriptor.  When two bytes are used to store an integer word, the least
  925. significant byte is kept in the lower memory address.  Therefore, the value
  926. 0002 is actually listed as 02 00 (CodeView adds an extra blank between
  927. bytes for clarity).
  928.      Immediately following the six bytes for the string "Hi" and its
  929. descriptor is another descriptor.  This one shows that the string has a
  930. length of 23 Hex bytes, and its data starts at address 4A Hex.  Again, the
  931. value 0023 is shown as 23 00, and the address 004A is displayed as 4A 00. 
  932. This string contains the data "ABCDEFGHIJKLMNOPQRSTUV".
  933.      The U (Unassemble) command can be used to show the assembly language
  934. source code at any arbitrary segment and address.  The command U 2000:1000
  935. will unassemble the code at address 2000:1000, though again you may need to
  936. use U &H2000:&H1000 in some view modes.  The U command is not used that
  937. frequently, since CodeView is used most often to step through code in
  938. sequence, rather than to examine an arbitrary block of instructions.
  939.      The R command lets you change the contents of a register, and this
  940. might be useful when debugging your own assembly language subroutines. 
  941. When you type, for example, RCX and press Enter, the current value of the
  942. CX register is displayed and you are prompted for a new value.  Pressing
  943. Enter alone cancels the command and leaves the current register contents
  944. intact.  Otherwise, the value you enter will be assigned to CX.  This is
  945. similar to BASIC's immediate window, in which you can assign new values to
  946. a variable.
  947.      The last CodeView features worth describing here are Watch Variables
  948. and Watch Points, which are similar to the same features in QB.  Unlike QB,
  949. though, you cannot use an expression as the target of a Watch; it must be a
  950. simple variable name, array element, or address.  Watch Variables may be
  951. added using the pull-down menu, or by pressing Alt-W and then typing the
  952. variable name.  If you are in the BASIC view mode you may add only BASIC
  953. variables; in the assembly language view mode you can add only assembly
  954. language variables.  To monitor the contents of a memory address requires
  955. the W command.  For example, W 40 will set up address 40 as the target of a
  956. Watch.
  957.      Although CodeView does support Watch points, whereby the program will
  958. run continuously until a given expression is true, you won't want to use
  959. that feature.  Asking CodeView to stop when, say, CX becomes greater than
  960. 100 will cause your program to run at less than one thousandth its normal
  961. speed.  Therefore, I have never found using Watch Points effective in any
  962. situation--it is always too slow.
  963.      I have avoided discussing the latest versions of CodeView, in favor of
  964. focusing on those features which are common to all versions.  CodeView 3.10
  965. which is included with BASIC 7.1 has several new convenience features, and
  966. a few new bugs as well.  Many of the commands that in earlier versions have
  967. to be entered manually are now available by simply typing new values onto
  968. the display.  For instance, where older versions of CodeView required you
  969. to enter Dump commands repeatedly, the new version updates the displayed
  970. values in a range of addresses constantly.  And to change the address
  971. range, you may now simply move the cursor to the segment and address
  972. numbers and type new ones.  An option to display memory values as words or
  973. even single and double precision values is also present in version 3.10.
  974.      Now that you have seen what CodeView is all about and how to use it, I
  975. want to conclude this chapter with a practical example.  As I mentioned in
  976. Chapter 3, the amount of stack memory that is needed in a non-static
  977. subprogram or function can be difficult to determine.  The calculation
  978. itself is trivial: simply add up the number of bytes needed by every
  979. variable in the routine.  Each integer requires two bytes, single
  980. precision, long integer, and string variables need four bytes, and so
  981. forth.  The problem, of course, is who wants to do all that counting,
  982. especially when there may be hundreds of variables.  Counting is what
  983. computers are for, no?
  984.      The solution is that BASIC knows how many bytes are needed for the
  985. subprogram, and the very first thing a subprogram does when it is invoked
  986. is to call another routine that allocates the necessary stack space.  So
  987. rather than use trial and error methods to increase the stack in small
  988. increments, you can use CodeView to directly see how many bytes of stack
  989. space are being requested.  Here's how that's done, using the example
  990. program shown below.
  991.  
  992.  
  993. DEFINT A-Z
  994. DECLARE SUB StackTest (Dummy)
  995. Test = 10
  996. CALL StackTest(Test)
  997. END
  998.  
  999. SUB StackTest(AnyVar)
  1000.   X = 100
  1001.   Y = 10
  1002.   Z = AnyVar
  1003. END SUB
  1004.  
  1005.  
  1006. Save this program as an ASCII file using the name TEST.BAS, and then
  1007. compile it with the /o and /zi options.  Next, link TEST.OBJ for CodeView
  1008. using the /co option.  Then start CodeView by entering CV TEST.  Once you
  1009. are in CodeView and viewing the BASIC source, press F10 to skip past
  1010. BASIC's start-up code.  At this point the cursor should be on the first
  1011. statement, Test = 10.  Finally, press F3 to show a mix of BASIC and
  1012. assembly language source code.  The display should look similar to that
  1013. shown in Figure 4-3 [unavailable].
  1014.  
  1015.  
  1016. FIG4-3: How to determine the amount of stack memory needed for a non-static
  1017. procedure.
  1018.  
  1019.  
  1020. Notice the first statement within the TestStack subprogram at line 7, where
  1021. the value 6 (erroneously labeled b$STRTAB+6) is assigned to the CX
  1022. register.  This is the number of bytes of stack space being requested from
  1023. the B$ENRA routine which is called in the next instruction.  B$ENRA is the
  1024. routine that actually allocates the stack memory, and it uses the value
  1025. BASIC sends in CX to know how many bytes are needed.  TestStack has three
  1026. local variables and each is a two-byte integer, hence six bytes are
  1027. required to store them on the stack.
  1028.      For a very large program, the value assigned to CX will of course be
  1029. much larger.  Further, if one subprogram calls another, it will be up to
  1030. you to add up all of the CX values to determine the total stack memory
  1031. requirements.  But this is very much easier than counting variables.
  1032.  
  1033.  
  1034. SUMMARY
  1035.  
  1036. In this chapter you have learned how to identify and correct common
  1037. programming errors.  You have also learned the importance of understanding
  1038. BASIC's various quirks, and how some statements do not always do exactly
  1039. what you thought they would.  I have shown several debugging strategies,
  1040. including a software adaptation of the "cut in half" hardware technique.
  1041.      Perhaps your most powerful debugging ally is the QuickBASIC and QBX
  1042. editing environments.  These powerful editors let you single step through a
  1043. program, monitor variable values and function results, and halt your
  1044. program when a specified condition occurs.
  1045.      When BASIC terminates a program prematurely with an error message and
  1046. a segmented address, you can either use the BC compiler's /a option to
  1047. generate a source listing, or use CodeView to see where the error occurred. 
  1048. CodeView can also be used to step and trace through a program at the
  1049. assembly language source level, and to determine the number of bytes of
  1050. stack memory a non-static procedure requires.
  1051.      In Chapter 5 you will learn about compiling and linking BASIC
  1052. programs.  I will present a complete overview of the many BC and LINK
  1053. options that are available, and discuss the relative merits of each.
  1054.